/**
 *
 * @param componentName   组件名称 String
 * @param eventName     事件名   {String|Object} event
 * @param params        数据 []  Array
 * @description  事件广播
 * 广播事件，通知给当前实例的全部后代。
 * 因为后代有多个枝杈，事件将沿着各“路径”通知。
 * 每条路径上的通知在触发一个监听器后停止，除非它返回 true。
 */
function broadcast (componentName, eventName, params) {
  /*
   * debugger
   * 循环所有子孙组件
   */
  this.$children.forEach(child => {
    /*
     *  debugger
     *  获取子组件name
     */
    const name = child.$options.name
    // 若组件名为componentName
    if (name === componentName) {
      /*
       * 在符合的子组件上触发的事件，但是不会再继续寻找符合名称的组件的子集，原因？
       * child.$emit.apply(child, [eventName].concat(params));
       */
      child.$emit.call(child, eventName, params)
    } else {
      /*
       *  如果 params 是空数组，接收到的会是 undefined,
       * 不符合继续寻找他的子集（即孙子组件）
       *  broadcast.apply(child, [componentName, eventName].concat([params]));
       */
      broadcast.call(child, componentName, eventName, params)
    }
  })
}
/**
 *
 * @param componentName   组件名称 String
 * @param eventName     事件名   {String|Object} event
 * @param params        数据 []  Array
 * @description  事件派发
 * 事件派发,依然是以寻找所有父级，直到找到要找的父组件，并在其身上触发指定事件。
 * 整体功能更类似jQuery的closest方法。
 */
function dispatch (componentName, eventName, params) {
  // 寻找父级组件
  let parent = this.$parent || this.$root
  //
  let name = parent.$options.name
  // 寻找父级，如果父级不是符合的组件名，则循环向上查找
  while (parent && (!name || name !== componentName)) {
    // 父级元素
    parent = parent.$parent
    // 寻找到父级，获取其组件名称
    if (parent) {
      name = parent.$options.name
    }
  }
  /// /找到符合组件名称的父级后，触发其事件。整体流程类似jQuery的closest方法
  if (parent) {
    //   parent.$emit.apply(parent, [eventName].concat(params));
    parent.$emit(eventName, params)
  }
}

export default {
  methods: {
    // 事件派发（向上）
    dispatch (componentName, eventName, params) {
      dispatch.call(this, componentName, eventName, params)
    },
    // 事件广播 （向下）
    broadcast (componentName, eventName, params) {
      broadcast.call(this, componentName, eventName, params)
    }
  }
}
